package com.moli.iov.util;

import com.moli.iov.model.Location;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 判断点是否多边形内
 * @author zql
 *
 */
public class GeoUtils {
	
	private static final Logger logger = LoggerFactory.getLogger(GeoUtils.class);

	private static final String LONGITUDE_KEY = "longitude";

	private static final String LATITUDE_KEY = "latitude";
	/**
	 * 判断点是否多边形内
	 * 基本思想是利用射线法，计算射线与多边形各边的交点，如果是偶数，则点在多边形外，否则
	 * 在多边形内。还会考虑一些特殊情况，如点在多边形顶点上，点在多边形边上等特殊情况。
	 * @param polygonList 多边形对象坐标List
	 * @param longitude 待测点的 经度
	 * @param latitude  待测点的   纬度
	 * @returns {Boolean} 点在多边形内返回true,否则返回false
	 */
	public static boolean isPointInPolygon(List<Map<String,Double>> polygonList, double longitude, double latitude){
		double x = latitude;
		double y = longitude;
		boolean result = false;
		try{
			if(polygonList!=null&&polygonList.size()>0){
				for (int i = 0, j = polygonList.size() - 1; i < polygonList.size(); j = i++) {
					Map<String,Double> p1 = polygonList.get(i);
					Map<String,Double> p2 = polygonList.get(j);
					double p1longitude = NumberUtil.stringToDouble(p1.get(LONGITUDE_KEY)==null?"0":String.valueOf(p1.get(LONGITUDE_KEY)), 0);
					double p2longitude = NumberUtil.stringToDouble(p2.get(LONGITUDE_KEY)==null?"0":String.valueOf(p2.get(LONGITUDE_KEY)), 0);
					double p1latitude = NumberUtil.stringToDouble(p1.get(LATITUDE_KEY)==null?"0":String.valueOf(p1.get(LATITUDE_KEY)), 0);
					double p2latitude = NumberUtil.stringToDouble(p2.get(LATITUDE_KEY)==null?"0":String.valueOf(p2.get(LATITUDE_KEY)), 0);
					if(p1longitude!=0&&p2longitude!=0&&p1latitude!=0&&p2latitude!=0){
						if ((p1longitude > y) != (p2longitude > y) &&
								(x < (p2latitude - p1latitude) * 
										(y - p1longitude) / 
										(p2longitude-p1longitude) 
										+ p1latitude)) {
								result = !result;
						}
					}
				}
			}
		}catch(Exception e){
			logger.error("判断点是否多边形内异常",e);
		}
		
		return result;
	}




	/**
	 *
	 * 根据中心点经纬度算出正方形的四个角的经纬度
	 * @param longitude    经度
	 * @param latitude    纬度
	 * @param distance    范围（米）
	 * @return
	 */
	public static Map<String, double[]> returnLLSquarePoint(double longitude,  double latitude, double distance) {
		Map<String, double[]> squareMap = new HashMap<String, double[]>();
		// 计算经度弧度,从弧度转换为角度
		double dLongitude = 2 * (Math.asin(Math.sin(distance
				/ (2 * 6378137))
				/ Math.cos(Math.toRadians(latitude))));
		dLongitude = Math.toDegrees(dLongitude);
		// 计算纬度角度
		double dLatitude = distance / 6378137;
		dLatitude = Math.toDegrees(dLatitude);
		// 正方形
		double[] leftTopPoint = { latitude + dLatitude, longitude - dLongitude };
		double[] rightTopPoint = { latitude + dLatitude, longitude + dLongitude };
		double[] leftBottomPoint = { latitude - dLatitude, longitude - dLongitude };
		double[] rightBottomPoint = { latitude - dLatitude, longitude + dLongitude };
		squareMap.put("leftTopPoint", leftTopPoint);
		squareMap.put("rightTopPoint", rightTopPoint);
		squareMap.put("leftBottomPoint", leftBottomPoint);
		squareMap.put("rightBottomPoint", rightBottomPoint);
		return squareMap;
	}

	/**
	 *
	 * 根据中心点经纬度算出正方形的四个角的经纬度
	 * @param longitude    经度
	 * @param latitude    纬度
	 * @param distance    范围（米）
	 * @return
	 */
	public static List<Location> returnLLSquarePointList(double longitude, double latitude, double distance) {
		List<Location> squareList = new ArrayList<>();
		// 计算经度弧度,从弧度转换为角度
		double dLongitude = 2 * (Math.asin(Math.sin(distance
				/ (2 * 6378137))
				/ Math.cos(Math.toRadians(latitude))));
		dLongitude = Math.toDegrees(dLongitude);
		// 计算纬度角度
		double dLatitude = distance / 6378137;
		dLatitude = Math.toDegrees(dLatitude);
		// 正方形
		Location leftTopPoint = new Location(latitude + dLatitude, longitude - dLongitude );
		Location rightTopPoint = new Location(latitude + dLatitude, longitude + dLongitude);
		Location leftBottomPoint = new Location(latitude - dLatitude, longitude - dLongitude );
		Location rightBottomPoint = new Location(latitude - dLatitude, longitude + dLongitude );
		squareList.add(leftTopPoint);
		squareList.add(rightTopPoint);
		squareList.add(rightBottomPoint);
		squareList.add(leftBottomPoint);
		return squareList;
	}

	private static double EARTH_RADIUS = 6378.137;
	private static double rad(double d)
	{
		return d * Math.PI / 180.0;
	}

	/**
	 * 计算是否在圆上(单位/千米)
	 * @param radius  半径
	 * @param lat1    维度  圆心
	 * @param lng1    经度  圆心
	 * @param lat2    维度  判断点
	 * @param lng2    经度  判断点
	 * @return
	 */
	public static boolean isInCircle(double radius, double lat1, double lng1, double lat2, double lng2){
		double s = getDistance(lat1, lng1, lat2, lng2);
		if(s > radius){
			//不在圆内
			return false;
		}else{
			return true;
		}
	}

	public static double getDistance(double lat1, double lng1, double lat2, double lng2)
	{
		double radLat1 = rad(lat1);
		double radLat2 = rad(lat2);
		double a = radLat1 - radLat2;
		double b = rad(lng1) - rad(lng2);
		double s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a/2),2) +
				Math.cos(radLat1)*Math.cos(radLat2)*Math.pow(Math.sin(b/2),2)));
		s = s * EARTH_RADIUS;
		s = Math.round(s * 10000);
		s = s / 10000;
		return s;
	}

	public static void main(String[] args) {
		double raduis = 0.456;
		double lat1 = 39.92435720459167;
		double lng1 = 116.40324146859298;
		double lat = 39.92429927604286;
		double lng = 116.40343445481159;
		boolean inCircle = isInCircle(raduis, lat1,lng1, lat, lng);
		System.out.println(inCircle);
	}
}
